﻿using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Input;
using CLEX.Service.DataContracts;

namespace CLEX.Client.Metro.ViewModel.Home
{
    public class DatabaseReadVM : VMBase
    {
        private readonly int databaseId;
        private readonly int userId;
        private readonly string server;
        private readonly string database;
        private readonly string connectionString;

        public int DatabaseId
        {
            get { return databaseId; }
        }

        public int UserId
        {
            get { return userId; }
        }

        public string Server
        {
            get { return server; }
        }

        public string Database
        {
            get { return database; }
        }

        public string ConnectionString
        {
            get { return connectionString; }
        }

        public DatabaseReadVM(int databaseId, int userId, string server, string database, string connectionString)
        {
            this.databaseId = databaseId;
            this.userId = userId;
            this.server = server;
            this.database = database;
            this.connectionString = connectionString;
        }
    }

    public class HomeVM : VMBase
    {
        private readonly IFacade facade;
        private readonly UserData user;
        private readonly ObservableCollection<DatabaseReadVM> databases;
        private DatabaseReadVM selectedDatabase;
        private readonly RelayCommand logout;
        private readonly RelayCommand newDatabase;
        private readonly RelayCommand exploreDatabase;
        private readonly RelayCommand editDatabase;
        private readonly RelayCommand deleteDatabase;
        private readonly RelayCommand changePassword;

        public ObservableCollection<DatabaseReadVM> Databases
        {
            get { return databases; }
        }

        public DatabaseReadVM SelectedDatabase
        {
            get { return selectedDatabase; }
            set
            {
                if (selectedDatabase != value)
                {
                    selectedDatabase = value;
                    RaisePropertyChanged("SelectedDatabase");
                    newDatabase.RaiseCanExecuteChanged();
                    exploreDatabase.RaiseCanExecuteChanged();
                    editDatabase.RaiseCanExecuteChanged();
                    deleteDatabase.RaiseCanExecuteChanged();
                }
            }
        }

        public ICommand Logout
        {
            get { return logout; }
        }

        public RelayCommand NewDatabase
        {
            get { return newDatabase; }
        }

        public ICommand ExploreDatabase
        {
            get { return exploreDatabase; }
        }

        public ICommand EditDatabase
        {
            get { return editDatabase; }
        }

        public ICommand DeleteDatabase
        {
            get { return deleteDatabase; }
        }

        public ICommand ChangePassword
        {
            get { return changePassword; }
        }

        public HomeVM(IFacade facade, UserData user)
        {
            this.facade = facade;
            this.user = user;
            this.databases = new ObservableCollection<DatabaseReadVM>(facade.GetDatabasesByUserId(user.UserId).Select(ToDatabaseReadVM));
            this.logout = new RelayCommand(DoLogout);
            this.newDatabase = new RelayCommand(DoNew);
            this.exploreDatabase = new RelayCommand(DoExplore, CanExplore);
            this.editDatabase = new RelayCommand(DoEdit, CanEdit);
            this.deleteDatabase = new RelayCommand(DoDelete, CanDelete);
            this.changePassword = new RelayCommand(DoChangePassword);

            facade.DatabaseCreated += Facade_DatabaseCreated;
            facade.DatabaseDeleted += Facade_DatabaseDeleted;
            facade.DatabaseUpdated += Facade_DatabaseUpdated;

            selectedDatabase = databases.FirstOrDefault();
        }

        private void Facade_DatabaseUpdated(DatabaseData database)
        {
            for (int i = 0; i < databases.Count; i++)
            {
                if (databases[i].DatabaseId == database.DatabaseId)
                {
                    databases[i] = ToDatabaseReadVM(database);
                    break;
                }
            }
        }

        private void Facade_DatabaseDeleted(DatabaseData database)
        {
            for (int i = 0; i < databases.Count; i++)
            {
                if (databases[i].DatabaseId == database.DatabaseId)
                {
                    databases.RemoveAt(i);
                    break;
                }
            }
        }

        private void Facade_DatabaseCreated(DatabaseData database)
        {
            databases.Add(ToDatabaseReadVM(database));
        }

        public static DatabaseReadVM ToDatabaseReadVM(DatabaseData database)
        {
            string databaseName;
            try
            {
                databaseName = Helper.GetDatabaseNameFromConnectionString(database.ConnectionString);
            }
            catch
            {
                databaseName = "<unkown>";
            }
            string serverName;
            try
            {
                serverName = Helper.GetServerNameFromConnectionString(database.ConnectionString);
            }
            catch
            {
                serverName = "<unkown>";
            }

            return new DatabaseReadVM(database.DatabaseId, database.UserId, serverName, databaseName, database.ConnectionString);
        }

        private void DoLogout()
        {
            facade.ShowLogout();
        }

        private void DoNew()
        {
            try
            {
                facade.ShowDatabaseNew(user);
            }
            catch (Exception ex)
            {
                facade.ShowUnexpectedError("Unable to create a database.", ex.Message);
            }
        }

        private void DoExplore()
        {
            try
            {
                facade.ShowTableList(user, new DatabaseData(selectedDatabase.DatabaseId, selectedDatabase.UserId, selectedDatabase.ConnectionString));
            }
            catch (Exception ex)
            {
                facade.ShowUnexpectedError("Unable to connect to the database.", ex.Message);
            }
        }

        private bool CanExplore()
        {
            return selectedDatabase != null;
        }

        private void DoEdit()
        {
            try
            {
                facade.ShowDatabaseEdit(new DatabaseData(selectedDatabase.DatabaseId, selectedDatabase.UserId, selectedDatabase.ConnectionString));
            }
            catch (Exception ex)
            {
                facade.ShowUnexpectedError("Unable to edit the database.", ex.Message);
            }
        }

        private bool CanEdit()
        {
            return selectedDatabase != null;
        }

        private void DoDelete()
        {
            try
            {
                facade.ShowDatabaseDelete(new DatabaseData(selectedDatabase.DatabaseId, selectedDatabase.UserId, selectedDatabase.ConnectionString));
            }
            catch (Exception ex)
            {
                facade.ShowUnexpectedError("Unable to delete the database.", ex.Message);
            }
        }

        private bool CanDelete()
        {
            return selectedDatabase != null;
        }

        private void DoChangePassword()
        {
            try
            {
                facade.ShowChangedPassword(user);
            }
            catch (Exception ex)
            {
                facade.ShowUnexpectedError("Unable to change the password.", ex.Message);
            }
        }
    }
}
